package com.sz.common.pay.pingan.core.server;

import com.sz.common.pay.common.DateGenerator;
import com.sz.common.pay.pingan.constant.Constant;
import com.sz.common.pay.pingan.exception.PayCommonException;
import com.sz.common.pay.pingan.util.CharSetUtil;

import java.util.HashMap;
import java.util.Optional;

/**
 * Function: 生成响应银行的报文 <br>
 *
 * @author: siqishangshu <br>
 * @date: 2017-12-19 15:53:00
 */
public class HttpResponseConverter {

    private static final int NET_LEN = 122;
    private static final String HEAD_LEN_FORMAT = "%08d";
    private static final String NET_LEN_FORMAT = "%010d";

    /**
     * 生成响应银行的完整报文
     *
     * @param parmaKeyDict
     * @return
     */
    public String getTranMessage(String sequence, HashMap parmaKeyDict) throws PayCommonException {

        byte[] byteMessageBody;
        String tranMessage = "";
        String tranMessageBody = "";
        int hTranFunc = 0;
        if (parmaKeyDict.containsKey(Constant.TRAN_FUNC)) {
            hTranFunc = Integer.parseInt((String)parmaKeyDict.get(Constant.TRAN_FUNC));
        }
        /*组业务报文体*/
        tranMessageBody = getTranMessageBody(sequence, parmaKeyDict, hTranFunc);

        byteMessageBody = CharSetUtil.getBytesFromStringToGBK(tranMessageBody);

      /*组公网业务报文头*/
        int iLength = byteMessageBody.length;
        String hLength = String.format(HEAD_LEN_FORMAT, iLength);
        String tranMessageHead = getTranMessageHead(sequence,hLength, parmaKeyDict);

      /*组公网通讯报文头*/
        int iNetLength = iLength + NET_LEN;
        String hNetLength = String.format(NET_LEN_FORMAT, iNetLength);
        String tranMessageNetHead = getTranMessageNetHead(sequence,hNetLength, parmaKeyDict);

      /*组完整请求报文*/
        tranMessage = tranMessageNetHead + tranMessageHead + tranMessageBody;

        return tranMessage;
    }


    /**
     * 生成请求银行报文的报文头
     * 接口报文包括：通讯报文头+业务报文头＋业务报文体。业务报文体见各交易定义，业务报文头统一如下，定长122位：
     * 编号	输入项名称	    英文名	    类型/长度	输入属性	注释
     * 1	    交易类型	        TranFunc	C(4)	必输	    见具体接口交易码
     * 2	    服务类型	        ServType	C(2)	必输	    报文的功能标识码（01:请求02:应答）
     * 3	    MAC码	        MacCode	    C(16)	必输
     * 4	    交易日期	        TranDate	C(8)	必输	    20080428代表2008年4月28日
     * 5	    交易时间	        TranTime	C(6)	必输	    010101代表1时1分1秒
     * 6	    应答码	        RspCode	    C(6)	必输
     * 7	    应答码描述	    RspMsg	    C(42)	必输
     * 8	    后续包标志	    ConFlag	    C(1)	必输
     * 9	    报文体长度	    Length	    C(8)	必输	    不包括报文头长度
     * 10	    操作员号	        CounterId	C(5)	必输	    未启用检验域，建议送00000
     * 11	    请求方系统流水号	ThirdLogNo	C(20)	必输	    本交易的流水号， 唯一标识一笔业务
     * 12	    交易网代码	    Qydm	    C(4)	必输
     *
     * @param hLength
     * @param parmaKeyDict
     * @return
     */

    private String getTranMessageHead(String sequence,String hLength, HashMap parmaKeyDict) throws PayCommonException {
        //交易类型
        String hTranFunc = (String) parmaKeyDict.get(Constant.TRAN_FUNC);
        //服务类型  报文的功能标识码（01:请求02:应答）
        String hServType = "02";
        //MAC码
        String hMacCode = "                ";
        //交易日期  20080428代表2008年4月28日
        String hTrandateTime = DateGenerator.getyyyyMMddHHmmss();
        //应答码
        String hRspCode = "999999";
        //应答码描述  42 个空格
        StringBuffer hRspMsg = new StringBuffer("                                          ");
        // 后续包标志  "0-结束包，1-还有后续包     目前仅支持0"
        String hConFlag = "0";
        //操作员号	 未启用检验域，建议送00000
        String hCounterId = "PA001";
        //请求方系统流水号	ThirdLogNo	C(20)	必输	    本交易的流水号， 唯一标识一笔业务
        String hThirdLogNo = sequence;
        //交易网代码	 C(4)	必输
        String hQydm = Constant.QYDM;

        if (parmaKeyDict.containsKey(Constant.RSP_CODE)) {
            hRspCode = (String) parmaKeyDict.get(Constant.RSP_CODE);
        }

        if (parmaKeyDict.containsKey(Constant.RSP_MSG)) {
            hRspMsg = new StringBuffer((String) parmaKeyDict.get(Constant.RSP_MSG));
        }
        int hLen = CharSetUtil.getBytesFromStringToGBK(hRspMsg.toString()).length;
        int iSpace = 42 - hLen;
        for (int i = 0; i < iSpace; i++) {
            hRspMsg.append(" ");
        }

        String tranMessageHead = hTranFunc + hServType + hMacCode + hTrandateTime + hRspCode + hRspMsg +
                hConFlag + hLength + hCounterId + hThirdLogNo + hQydm;

        return tranMessageHead;
    }

    /**
     * 如下为固定长度222 字节“通信报文头”：报文头部分报文组成
     * 编号	    域名	        长度和属性	 输入属性	    备注
     * 1	    报文版本	    Char(4)	    必输	    "A001定长222报文头+报文体，客户端需要按此报文头上送。     A002 无报文头，客户端不上送以A001开头的报文，由B2BiC客户端增加A002开头的222位报文头"
     * 2	    目标系统	    Char (2)	必输  	"银行业务系统在B2Bi分配的ID。请咨询银行接口负责人（或者查看目标系统的接口文档），获取正确的目标ID值。建议此域做成配置参数"
     * 3	    报文编码	    Char (2)	必输	    "01：GBK缺省 02：UTF8     03：unicode     04：iso-8859-1     建议使用GBK编码"
     * 4	    通讯协议	    Char (2)	必输  	"01:tcpip 缺省  02：http    03：webservice"
     * 5	    外联客户代码	Char (20)	必输	    "测试使用联系银行获取，企业上线的时候由银行分配，请联系客户经理获取；     建议此域做成配置参数"
     * 6	    接收报文长度	Num(10)	    必输	    报文体数据的字节长度（提示：不是字符串的长度）；不包括A001头的222位、附件内容、签名内容的长度
     * 7	    交易码	    Char(6)	    Y	    "请按目标系统发布的接口文档实际填写， 若无需要默认填写000000  建议此域做成配置参数"
     * 8	    操做员代码	Char (5)	N	    未启用检验域，建议送00000
     * 9	    服务类型	    Char(2)	    N	    "1 请求     2 应答"
     * 10	    交易日期	    Char(8)	    Y	    yyyymmdd
     * 11	    交易时间	    Char(6)	    Y	    hhmmss
     * 12	 请求方系统流水号	Char(20)	Y	    "唯一标识一笔交易（企业随机生成唯一流水号）     备注：（如果某种交易要有多次请求的才能完成的，多个交易请求包流水号要保持一致）"
     * 13	    返回码	    Char(6)	    Y	    "请求时必须填写000000     非“000000”代表交易受理异常或失败"
     * 14	    返回描述	    Char(100)	N	    格式为 “:交易成功”；其中冒号为英文格式半角。
     * 15	    后续包标志	Char(1)	    N	    "0-结束包，1-还有后续包     目前仅支持0"
     * 16	    请求次数	    Num (3)	    N	    "如果有后续包请求     000     001     002依此增加     目前仅支持000"
     * 17	    签名标识	    Char(1)	    N	    "不签名     签名     目前仅支持填0"
     * 18	  签名数据包格式	Char(1)	    N	    "裸签     PKCS7     目前仅支持送1"
     * 19	    签名算法	    Char(12)	N	    RSA-SHA1
     * 20	    签名数据长度	Num (10)	Y	    签名报文数据长度, 目前仅支填写0
     * 21	    附件数目	    Num(1)	    Y	    没有,默认为0；有的话填具体个数，最多9个
     * <p>
     * 注释：如果非必输域需要用空格补齐报文；填充不满的，数字类型右对齐，签名填0；字符类型左对齐，后面补空格。
     * 特别提示：
     * 报文头中的“目标系统”、“外联客户代码”、“交易码”建议做成参数化配置，不能硬编码。因为测试、生产是不同的外联客户代码；
     * 不同的交易对应的“交易码”不同；若企业要对接银行的多个业务系统，“目标系统”也是不通的。
     * 通讯报文头举例：
     * A0011301013006................0000000232000000admin0120150803171539101438571461163100..999999......................
     * ..............................................................................000001............00000000000
     * （注意：此处用点代替空格，仅为进行报文讲解，为更清晰展示报文构成。实际报文里必须是空格，用点会报错。）
     * 域名	            长度和属性	            内容
     * 报文版本	            Char(4)	                A001
     * 目标系统	            Char (2)	            13
     * 报文编码	            Char (2)	            01
     * 通讯协议	            Char (2)	            01
     * 外联客户代码	        Char (20)	            3006
     * 接收报文长度	        Num(10)	                0000000232
     * 交易码	            Char(6)	                000000
     * 操做员代码	        Char (5)	            admin
     * 服务类型	            Char(2)	                01
     * 交易日期	            Char(8)	                20150803
     * 交易时间	            Char(6)	                171539
     * 请求方系统流水号	    Char(20)	            101438571461163100
     * 返回码	            Char(6)	                999999
     * 返回描述           	Char(100)	            100个空格
     * 后续包标志	        Char(1)	                0
     * 请求次数	            Num (3)	                000
     * 签名标识	            Char(1)	                0
     * 签名数据包格式	    Char(1)	                0
     * 签名算法           	Char(12)	            12个空格
     * 签名数据长度	        Num (10)	            0000000000
     * 附件数目	            Num(1)	                0
     *
     * @param sequence     操作流水号
     * @param hLength      接收报文长度
     * @param parmaKeyDict 数据
     * @return
     */
    private String getTranMessageNetHead(String sequence,String hLength, HashMap parmaKeyDict) throws PayCommonException {

        //报文版本+目标系统+报文编码+通讯协议
        // "A001 03 0102";
        String netHeadPart1 = Constant.MESSAGE_VERSION + Constant.TARGET_SYSTEM + Constant.CHARSET_CODE + Constant.COMMUNICATION_PROTOCOL_SERVER;
        //外联客户代码 +  16个空格 加上 hQydm 正好20位
        String netHeadPart2 = "                ";
        String hQydm = Constant.QYDM;
        //交易码 "请按目标系统发布的接口文档实际填写， 若无需要默认填写000000 "
        //"000000";
        String hTradeCode = Constant.TRADE_CODE;

        //操做员代码
        String hCounterId = "PA001";
        //服务类型    "01 请求     02 应答"
        String hServType = "02";
        //String hMacCode="                ";
        // 交易日期	 yyyymmdd
        String hTrandateTime = DateGenerator.getyyyyMMddHHmmss();
        //交易时间  hhmmss

        //请求方系统流水号  "唯一标识一笔交易（企业随机生成唯一流水号）     备注：（如果某种交易要有多次请求的才能完成的，多个交易请求包流水号要保持一致）"
        String hThirdLogNo = sequence;
        //返回码	 "请求时必须填写000000     非“000000”代表交易受理异常或失败"
        String hRspCode = "999999";
        //返回描述	    Char(100)   格式为 “:交易成功”；其中冒号为英文格式半角。//100个空格
        StringBuffer hRspMsg = new StringBuffer("                                                                                                    ");
        //后续包标志	Char(1)	     "0-结束包，1-还有后续包     目前仅支持0"
        String hConFlag = "0";
        //请求次数	    Num (3)	       "如果有后续包请求     000,   001 ,  002依此增加     目前仅支持000"
        String hTimes = "000";
        //签名标识	    Char(1)	    N	    "1不签名    2 签名     目前仅支持填0"
        String hSignFlag = "0";
        // 签名数据包格式	Char(1)	    N	    "1 裸签    2  PKCS7     目前仅支持送1"
        String hSignPacketType = "0";

        //签名算法	    Char(12)	N	    RSA-SHA1
        //12个空格
        String netHeadPart3 = "            ";
        //签名数据长度
        String netHeadPart4 = "00000000000";
        if (parmaKeyDict.containsKey(Constant.RSP_CODE)) {
            hRspCode = (String) parmaKeyDict.get(Constant.RSP_CODE);
        }

        if (parmaKeyDict.containsKey(Constant.RSP_MSG)) {
            hRspMsg = new StringBuffer((String) parmaKeyDict.get(Constant.RSP_MSG));
        }
        int hLen = CharSetUtil.getBytesFromStringToGBK(hRspMsg.toString()).length;
        int iSpace = 100 - hLen;
        for (int i = 0; i < iSpace; i++) {
            hRspMsg.append(" ");
        }
        String tranMessageNetHead = netHeadPart1 + hQydm + netHeadPart2 + hLength + hTradeCode + hCounterId + hServType + hTrandateTime + hThirdLogNo +
                hRspCode + hRspMsg + hConFlag + hTimes + hSignFlag + hSignPacketType + netHeadPart3 + netHeadPart4;

        return tranMessageNetHead;
    }

    /**
     * 根据交易码调用不同的交易请求报文生成方法
     *
     * @param parmaKeyDict
     * @param hTranFunc
     * @return
     * @throws PayCommonException
     */
    private String getTranMessageBody(String thirdLogNo, HashMap parmaKeyDict, int hTranFunc) throws PayCommonException {
        String tranMessageBody = "";
        switch (hTranFunc) {
            case 6000:
                //会员子账户开立 【6000】
//                tranMessageBody = getTranMessageBody_6000(parmaKeyDict);
                break;
            case 6005:
                //会员提现【6005】 （验证）
//                tranMessageBody = getTranMessageBody_6033(parmaKeyDict);
                break;
            //TODO NEW
            case 1303:
                //会员签解约维护【1303】
                tranMessageBody = getTranMessageBody_ThirdLogNo(parmaKeyDict);
                break;
            case 1310:
                //入金（银行发起）【1310】
                tranMessageBody = getTranMessageBody_ThirdLogNo(parmaKeyDict);
                break;
            case 1312:
                //出金（银行发起）【1312】
                tranMessageBody = getTranMessageBody_ThirdLogNo(parmaKeyDict);
                break;
            case 1326:
                //银行复核通知【1326】
                tranMessageBody = getTranMessageBody_ThirdLogNo(parmaKeyDict);
                break;
            default:
                tranMessageBody = thirdLogNo + "&";
        }

        return tranMessageBody;
    }

    private String getTranMessageBody_ThirdLogNo(HashMap parmaKeyDict) {

        String ThirdLogNo = Optional.ofNullable((String) parmaKeyDict.get("ThirdLogNo")).orElse("");
        String Reserve = Optional.ofNullable((String) parmaKeyDict.get("Reserve")).orElse("");

        String tranMessage = ThirdLogNo + "&" +
                Reserve + "&";
        return tranMessage;
    }

}
